package phototagger.imgsim;

import java.util.Iterator;
import java.util.ArrayList;

/** The YIQSignature class contains the data and methods to represent and
    deal with Eikon's wavelet decomposition image signatures. It is basically
    the same as the old TruncatedDecomposedImage class, with a better
    name. It also is more abstract, with fields for colorspace, signature size
    and wavelet decomposition type.

    <p>Signature objects can be created from their XML representations, 
    turned into an XML representation, inserted into the database, and used 
    by Query objects to get results from the database.    
**/

public class YIQSignature {
    public static final String COLORSPACE = "YIQ";

    // need a way to get these values from the interface...
    public static final double WEIGHT_Y_0 = 5.00;
    public static final double WEIGHT_Y_1 =  .83;
    public static final double WEIGHT_Y_2 = 1.01;
    public static final double WEIGHT_Y_3 =  .52;
    public static final double WEIGHT_Y_4 =  .47;
    public static final double WEIGHT_Y_5 =  .30;
    
    public static final double[] WEIGHTS_Y = {
	WEIGHT_Y_0, WEIGHT_Y_1, WEIGHT_Y_2, WEIGHT_Y_3, 
	WEIGHT_Y_4, WEIGHT_Y_5};
    
    public static final double WEIGHT_I_0 = 19.21;
    public static final double WEIGHT_I_1 =  1.26;
    public static final double WEIGHT_I_2 =   .44;
    public static final double WEIGHT_I_3 =   .53;
    public static final double WEIGHT_I_4 =   .28;
    public static final double WEIGHT_I_5 =   .14;
    
    public static final double[] WEIGHTS_I = {
	WEIGHT_I_0, WEIGHT_I_1, WEIGHT_I_2, WEIGHT_I_3, 
	WEIGHT_I_4, WEIGHT_I_5};

    public static final double WEIGHT_Q_0 = 34.37;
    public static final double WEIGHT_Q_1 =   .36;
    public static final double WEIGHT_Q_2 =   .45;
    public static final double WEIGHT_Q_3 =   .14;
    public static final double WEIGHT_Q_4 =   .18;
    public static final double WEIGHT_Q_5 =   .27;
    
    public static final double[] WEIGHTS_Q = {
	WEIGHT_Q_0, WEIGHT_Q_1, WEIGHT_Q_2, WEIGHT_Q_3, 
	WEIGHT_Q_4, WEIGHT_Q_5};
    
    private int[][] Ycoeffients;
    private int[][] Icoeffients;
    private int[][] Qcoeffients;
    
    private double Yavg;
    private double Iavg;
    private double Qavg;

    private String waveletBasis;
    private int width;
    private int height;

    //Create a new YIQSignature object from a YIQImage object. 
    // FIXME: THIS ISN'T GOING TO WORK. Image holds doubles. Do I need this?
    // YAGNI? :b
//     public YIQSignature(YIQImage image, String basis) {
// 	Ycoeffients = image.getElements('Y');
// 	Icoeffients = image.getElements('I');
// 	Qcoeffients = image.getElements('Q');

// 	Yavg = image.getAverage('Y');
// 	Iavg = image.getAverage('I');
// 	Qavg = image.getAverage('Q');

// 	waveletBasis = basis;

// 	colors.add("Y");
// 	colors.add("I");
// 	colors.add("Q");

// 	width  = image.getWidth();
// 	height = image.getHeight();
//     }
	
    /** Create a new YIQSignature object.
	@param colorA the first color channel (in this case, Y)
	@param colorB the second color channel (I)
	@param colorC the third color channel (Q)
	@param Aavg the average of the first color channel (Y)
	@param Bavg the average of the second color channel (I)
	@param Cavg the average of the third color channel (Q)
     **/
    public YIQSignature(int[][] Ycoef, int[][] Icoef, int[][] Qcoef, 
	      double Yavg, double Iavg, double Qavg) {
	
	Ycoeffients = Ycoef;
	Icoeffients = Icoef;
	Qcoeffients = Qcoef;

	this.Yavg = Yavg;
	this.Iavg = Iavg;
	this.Qavg = Qavg;

	// I *think* this is right. I can never remember.
	width  = Ycoeffients[0].length;
	height = Ycoeffients.length;
    }

    public int[][] getCoeffients(String colorChannel)
	throws Exception {

	return getCoeffients(stringToChar(colorChannel));
    }

    public int[][] getCoeffients(char colorChannel)
	throws Exception {
	
	switch(colorChannel) {
	case 'Y':
	    return Ycoeffients; 
	case 'I':
	    return Icoeffients; 
	case 'Q':
	    return Qcoeffients; 
	default:
	    throw new Exception();
	}
	
    }

    public double getAverage(String colorChannel)
	throws Exception {

	return getAverage(stringToChar(colorChannel));
    }
    
    public double getAverage(char colorChannel)     
	throws Exception {
	
	switch(colorChannel) {
	case 'Y':
	    return Yavg; 
	case 'I':
	    return Iavg; 
	case 'Q':
	    return Qavg; 
	default:
	    throw new Exception();
	    
	}
    }

    public String getColorSpace() {
	return COLORSPACE;
    }

    public Iterator getColors() {
	ArrayList colors = new ArrayList();
	colors.add("Y");
	colors.add("I");
	colors.add("Q");
	
	return colors.iterator();
    }

    public String getWaveletBasis() {
	return waveletBasis;
    }

    public int getWidth() {
	return width;
    }

    public int getHeight() {
	return height;
    }

    public double[] getWeights(String color)
	throws Exception {

	return getWeights(stringToChar(color));
    }
    
    public static char stringToChar(String s) {
    	if (s.length() > 1) {
    	    String msg = s + " has more than one character. This is illegal.";
    	    throw new IllegalArgumentException(s);
    	}
    	return s.charAt(0);
    }
    
    public double[] getWeights(char color)
	    throws Exception {
	switch(color) {
	case 'Y':
	    return WEIGHTS_Y; 
	case 'I': 
	    return WEIGHTS_I; 
	case 'Q':
	    return WEIGHTS_Q; 
	default:
	    throw new Exception();
	}
    }
    
    public String toXml() {
	return "Not done yet!";
    }

    /** Gives a String representation of the YIQSignature. This returns
	the same datas as toXML, but in an unstructure form more suitable
	for debugging.
	@returns a big long string with all the info in this Signature
	@see toXML
    **/
    public String toString() {
	String s = "----\n";
	s += "Wavelet Basis: " + waveletBasis + "\n";
	s += "Color Space:   " + COLORSPACE + "\n";
	s += "Y Average:     " + Yavg + "\n";
	s += "I Average:     " + Iavg + "\n";
	s += "Q Average:     " + Qavg + "\n";
	s += "Y Coeffients\n";
	s += "------------\n";
	
	// TODO: I should write a helper function that makes this easy

	for (int i = 0; i < Ycoeffients.length; i++) {
	    for(int j = 0; j < Ycoeffients[0].length; j++) {
		if (Ycoeffients[i][j] != 0) {
		    s += "(x=" + i + ", y="+ j + ") ";
		    s += "value = " + Ycoeffients[i][j] + "\n";
		}
	    }
	}

	s += "I Coeffients\n";
	s += "------------\n";

	for (int i = 0; i < Icoeffients.length; i++) {
	    for(int j = 0; j < Icoeffients[0].length; j++) {
		if (Icoeffients[i][j] != 0) {
		    s += "(x=" + i + ", y="+ j + ")";
		    s += "value = " + Icoeffients[i][j] + "\n";
		}
	    }
	}

	s += "Q Coeffients\n";
	s += "------------\n";

	for (int i = 0; i < Qcoeffients.length; i++) {
	    for(int j = 0; j < Qcoeffients[0].length; j++) {
		if (Qcoeffients[i][j] != 0) {
		    s += "(x=" + i + ", y="+ j + ")";
		    s += "value = " + Qcoeffients[i][j] + "\n";
		}
	    }
	}

	return s;
    }

}
